package com.ikingtech.framework.sdk.authenticate.embedded.authenticate;

import com.ikingtech.framework.sdk.authenticate.embedded.core.Credential;
import com.ikingtech.framework.sdk.authenticate.extension.IdentityExtensionLoader;
import com.ikingtech.framework.sdk.cache.constants.CacheConstants;
import com.ikingtech.framework.sdk.context.security.Identity;
import com.ikingtech.framework.sdk.enums.system.variable.AllowMultiSignInEnum;
import com.ikingtech.framework.sdk.enums.system.variable.VariableEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.ikingtech.framework.sdk.context.constant.SecurityConstants.TOKEN_PREFIX;

/**
 * 身份认证基类
 *
 * @author tie yan
 */
@RequiredArgsConstructor
public abstract class AbstractAuthenticate implements Authenticate {

    protected final StringRedisTemplate redisTemplate;

    private final List<IdentityExtensionLoader> loaders;

    public void resolveMultiSign(final String userId, String endpoint) {
        String multiSignStrategy = VariableEnum.resolveAllowMultiSignIn(this.redisTemplate.opsForHash().get(CacheConstants.SYSTEM_VARIABLE, VariableEnum.ALLOW_MULTI_SIGN_IN.name()));
        if (Tools.Str.isBlank(multiSignStrategy) || AllowMultiSignInEnum.ALLOW.name().equals(multiSignStrategy)) {
            return;
        }
        this.redisTemplate.delete(Tools.Coll.convertList(this.redisTemplate.opsForList().range(CacheConstants.loginUserFormat(userId, endpoint), 0, -1),
                CacheConstants::accessTokenKeyFormat));
        this.redisTemplate.delete(CacheConstants.loginUserFormat(userId, endpoint));
    }

    @Override
    public Identity verify(Credential credential) {
        Identity identity = this.doVerify(credential, Tools.Id.uuid());
        // 根据注册中心的服务名称,补充各业务需要在首次登录时写入的数据,主要用于各业务方
        identity.setExtensions(this.loadIdentityExtension(identity));
        identity.setPassword(Tools.Str.EMPTY);
        return this.verifyPass(identity);
    }

    abstract Identity doVerify(Credential credential, String token);

    protected Identity verifyPass(Identity identity) {
        identity.setAuthenticated(true);
        long passwordExpire = VariableEnum.resolvePasswordExpire(
                this.redisTemplate.opsForHash().get(CacheConstants.SYSTEM_VARIABLE, VariableEnum.PASSWORD_EXPIRE.name()));
        //设置密码是否过期
        identity.setPasswordExpired(null != identity.getPasswordModifyTime() && LocalDateTime.now().minusDays(passwordExpire).isAfter(identity.getPasswordModifyTime()));

        long tokenExpire = VariableEnum.resolveTokenExpire(
                this.redisTemplate.opsForHash().get(CacheConstants.SYSTEM_VARIABLE, VariableEnum.TOKEN_EXPIRE.name()));

        // 根据不同的endpoint分别存储token信息
        String loginUserFormatCacheKey = CacheConstants.loginUserFormat(identity.getId(), identity.getEndpoint());
        this.redisTemplate.opsForList().rightPush(loginUserFormatCacheKey, identity.getToken());
        // 设置过期时间
        this.redisTemplate.expire(loginUserFormatCacheKey, tokenExpire, TimeUnit.MINUTES);
        // 根据token缓存用户信息
        this.redisTemplate.opsForValue().set(CacheConstants.accessTokenKeyFormat(identity.getToken()), Tools.Json.toJsonStr(identity), tokenExpire, TimeUnit.MINUTES);
        // 格式化token
        identity.setToken(TOKEN_PREFIX + identity.getToken());
        return identity;
    }

    private Map<String, Object> loadIdentityExtension(Identity identity) {
        Map<String, Object> result = new HashMap<>();
        if (Tools.Coll.isBlank(this.loaders)) {
            return new HashMap<>();
        }
        this.loaders.forEach(loader -> result.putAll(loader.load(identity)));
        return result;
    }
}
